
# 定义函数，不用写返回值，参数类型，函数体不能有大括号, 如果没有return语句，函数执行完毕后也会返回结果，只是结果为None。return None可以简写为return
print("\n--------------- 普通函数")
def get_min(x, y):
#{
#    if not isinstance(x, (int, float)):
#        raise TypeError('bad operand type..........')
    if x > y:
        return y
    else:
        return x
#}
print(get_min(2, -3))


# 空函数, pass语句什么都不做，那有什么用？实际上pass可以用来作为占位符，比如现在还没想好怎么写函数的代码，就可以先放一个pass，让代码能运行起来
print("\n----------------- 空函数")
def get_max(x, y):
    pass
print(get_max(2, -3))   # 返回 None


# 参数检查，调用函数时，如果参数个数不对，Python解释器会自动检查出来，并抛出TypeError
#print(get_min(1, 2, 3))     # TypeError: get_min() takes 2 positional arguments but 3 were given
#print(get_min('a', 2))      # TypeError: unorderable types: str() > int()
print(get_min('a', 'b'))     # 类型不对，但是可以比较，返回 a


# 返回多个值, 是一个tuple, 但是在语法上，返回一个tuple可以省略括号，而多个变量可以同时接收一个tuple，按位置赋给对应的值
print("\n-------------- 返回多个值")
def get_min_max(x, y):
    if x > y:
        return y, x
    else:
        return x, y
print(get_min_max(2, -3))   # 返回 (-3, 2)


# 默认参数, 必选参数在前，默认参数在后，否则Python的解释器会报错, 当函数有多个参数时，把变化大的参数放前面，变化小的参数放后面。变化小的参数就可以作为默认参数
# Python的函数定义非常简单，但灵活度却非常大。除了正常定义的必选参数外，还可以使用默认参数、可变参数和关键字参数，使得函数定义出来的接口，不但能处理复杂的参数，还可以简化调用者的代码
print("\n---------------- 默认参数")
def power(x, n=1):
    s = 1

    while n > 0:
        n -= 1
        s = s * x
    return s
print(power(3))     # 3
print(pow(3, 2))    # 9
print(pow(3, 3))    # 27

# 默认参数必须指向不变对象
def add_end(L=None):
    if L is None:
        L = []
    L.append("end")
    return L
print(add_end(['sky']))     # 不使用默认参数
print(add_end())            # 使用默认参数


# 可变参数 定义可变参数和定义一个list或tuple参数相比，仅仅在参数前面加了一个*号，可变参数允许你传入0个或任意个参数，这些可变参数在函数调用时自动组装为一个tuple
# a2 + b2 + c2 + ……
#def cal(numbers):  # 只适合传入list
print("\n---------------- 可变参数")
def cal(*numbers):
    sum = 0
    print("param_size=%s" % len(numbers))

    for num in numbers:
        sum += num * num
    return sum
#print(cal([1, 2]))
print(cal(1, 2))        # 5
print(cal(1, 2, 3))     # 14
num = [1, 2, 3, 4]
print(cal(*num))        # 30 把list转为可变参数序列

# 关键字参数 关键字参数允许你传入0个或任意个含参数名的参数，这些关键字参数在函数内部自动组装为一个dict(map). 它可以扩展函数的功能
print("\n-------------- 关键字参数")
def person(name, age, **kw):
    if 'city' in kw:
        pass
    if 'job' in kw:
        pass
    print('name:', name, 'age:', age, 'other:', kw)


person('sky', 25)                                   # 省略了关键字参数 name: sky age: 25 other: {}
person('sky', 25, addr='BaoAn')                     # 传一个关键字参数 name: sky age: 25 other: {'addr': 'BaoAn'}
person('sky', 25, city='ShenZhen', addr='BaoAn')    # 传两个关键字参数 name: sky age: 25 other: {'city': 'ShenZhen', 'addr': 'BaoAn'}


# 命名关键字参数 如果要限制关键字参数的名字，就可以用命名关键字参数，例如只接收city和job作为关键字参数, 和关键字参数**kw不同，
# 命名关键字参数需要一个特殊分隔符*，*后面的参数被视为命名关键字参数，如果有可变参数，可以不用加特殊分隔符*
print("\n----------- 命名关键字参数")
def person(name, age, *, city='China', job):
    print(name, age, city, job)

# TypeError: person() missing 2 required keyword-only arguments: 'city' and 'job'
#person('lotus', 25)
# 一定要以这种方式调用，前面两个是位置参数，后面两个是关键字参数，都是必须的
# 关键字参数必须传入参数名 TypeError: person() takes 2 positional arguments but 4 were given
#person('lotus', 25)                                # 关键字参数必须传入
#person('lotus', 25, 'XiAn', 'Teacher')             # 关键字参数必须传入
person('lotus', 25, city='XiAn', job='Teacher')     # 关键字参数必须传入
person('lotus', 25, job='Teacher', city='ShanXi')   # 关键字参数city和job的顺序无所谓
person('lotus', 25, job='Teacher')                  # 如果给city指定了默认值，可以不必传入

# 参数组合：在Python中定义函数，可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数，这5种参数都可以组合使用
# 但是请注意，参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数
# 虽然可以组合多达5种参数，但不要同时使用太多的组合，否则函数接口的可理解性很差